﻿using System.Collections.Generic;
using JetBrains.Annotations;
using Styx;
using Styx.TreeSharp;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

namespace RestoArenaHelper
{
    [UsedImplicitly]
    internal class CachedUnits : CacheManager
    {
        // Cache tunning -- milliseconds
        private const int HeallistExpiry = 1000;
        private const int TANK_EXPIRY = 1000;
        private const int TANKLIST_EXPIRY = 1000;
        public static List<WoWPlayer> TankList;

        // Cached Lists        
        public static List<WoWPlayer> HealList;

        public static void Pulse()
        {
            
        UpdateCache();

        }

        public static void Initialize()
        {
            if (!UpdateCache())
            {
                return;
            }

            HealList = new List<WoWPlayer>();
            AttachCombatLogEvent();
            RestoArenaHelper.OnWoWContextChanged += HandleContextChanged;
        }

        private static WoWPlayer CachedTank
        {
            get
            {
                const string CACHEKEY = "Tank";

                // Check the cache
                var Tank = Get<WoWPlayer>(CACHEKEY);

                if (Tank == null)
                {
                    // Go and retrieve the data from the objectManager
                    Tank = HealManager.GetTank;

                    // Then add it to the cache so we
                    // can retrieve it from there next time
                    // set the object to expire
                    Add(Tank, CACHEKEY, TANK_EXPIRY);
                }
                return Tank;
            }
        }

        private static List<WoWPlayer> CachedTankList
        {
            get
            {
                const string CACHEKEY = "Tanks";

                // Check the cache
                var tankList = Get<List<WoWPlayer>>(CACHEKEY);

                if (tankList == null)
                {
                    // Go and retrieve the data from the objectManager
                    tankList = HealManager.Tanks;

                    // Then add it to the cache so we
                    // can retrieve it from there next time
                    // set the object to expire
                    Add(tankList, CACHEKEY, TANKLIST_EXPIRY);
                }
                return tankList;
            }
        }

        public static bool UpdateCache()
        {
            UpdateCachedPlayers();
            return true;
        }

        private static void UpdateCachedPlayers()
        {
            HealList = CachedHealList; 
            RemoveFilter(HealList);
            IncludeFilter(HealList);
            TankList = CachedTankList;  
            HealManager.Tank = CachedTank;
        }

        #region Filtering

        private static bool BadPlayers(WoWObject o)
        {
            WoWPlayer p = o.ToPlayer();

            if (p == null)
            {
                return true;
            }

            if (!p.IsValid)
            {
                return true;
            }

            if (p.IsDead)
            {
                return true;
            }

            if (p.IsHostile)
            {
                return true;
            }

            if (p.DistanceSqr > HealManager.MaxHealingDistanceSqr)
            {
                return true;
            }

            if (p.IsGhost)
            {
                return true;
            }

            if (!p.IsMe && !p.IsInMyPartyOrRaid)
            {
                return true;
            }

            if (!p.IsMe && !p.InLineOfSight) 
            {
                return true;
            }

            return false;
        }

        private static bool GoodPlayers(WoWObject o)
        {
            return o != null && o.IsMe;
        }

        private static void RemoveFilter(List<WoWPlayer> units)
        {
            units.RemoveAll(BadPlayers);
        }

        private static void IncludeFilter(List<WoWPlayer> units)
        {
            if (!units.Exists(GoodPlayers))
            {
                HealList.Add(StyxWoW.Me);
            }
        }

        #endregion

        #region Cache Checks

        private static List<WoWPlayer> CachedHealList
        {
            get
            {
                const string cachekey = "HealList";

                // Check the cache
                var healList = Get<List<WoWPlayer>>(cachekey);

                if (healList == null)
                {
                    // Go and retrieve the data from the objectManager
                    healList = HealManager.GetInitialList;

                    // Then add it to the cache so we
                    // can retrieve it from there next time
                    // set the object to expire
                    Add(healList, cachekey, HeallistExpiry);
                }
                return healList;
            }
        }

        #endregion

        #region Combat Log Events

        // this is here to update only at specific times.

        private static bool _eventsAttached;

        private static void AttachCombatLogEvent()
        {
            if (_eventsAttached)
                return;
            Lua.Events.AttachEvent("PARTY_MEMBERS_CHANGED", HandleEvents);
            Lua.Events.AttachEvent("UNIT_NAME_UPDATE", HandleEvents);
            Lua.Events.AttachEvent("ZONE_CHANGED_NEW_AREA", HandleEvents);
            Lua.Events.AttachEvent("ZONE_CHANGED", HandleEvents);
            Lua.Events.AttachEvent("ROLE_CHANGED_INFORM", HandleEvents);
            Lua.Events.AttachEvent("UNIT_CONNECTION", HandleEvents);
            Lua.Events.AttachEvent("GROUP_ROSTER_UPDATE", HandleEvents);
            _eventsAttached = true;
        }

        private static void HandleEvents(object sender, LuaEventArgs args)
        {
            new CombatLogEventArgs(args.EventName, args.FireTimeStamp, args.Args);

            switch (args.EventName)
            {
                case "PARTY_MEMBERS_CHANGED":
                case "UNIT_NAME_UPDATE":
                case "ZONE_CHANGED_NEW_AREA":
                case "ZONE_CHANGED":
                case "ROLE_CHANGED_INFORM":
                case "UNIT_CONNECTION":
                case "GROUP_ROSTER_UPDATE":
                    UpdateCache();
                    break;
            }
        }

        private static void HandleContextChanged(object sender, WoWContextEventArg e)
        {
            UpdateCache();
        }

        #endregion Combat Log Events
    }
}